/*
    This file is part of QTau
    Copyright (C) 2013-2018  Tobias "Tomoko" Platen <tplaten@posteo.de>
    Copyright (C) 2013       digited       <https://github.com/digited>
    Copyright (C) 2010-2013  HAL@ShurabaP  <https://github.com/haruneko>

    QTau is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    SPDX-License-Identifier: GPL-3.0+
*/

#include "maddewindow.h"
#include <QCheckBox>
#include <QFileDialog>
#include <QKeyEvent>
#include <QLabel>
#include <QMessageBox>
#include <QSettings>
#include "ui_maddewindow.h"

#include <formantmapwindow.h>

#define __devloglevel__ 6

MaddeWindow::MaddeWindow(QWidget* parent)
    : QMainWindow(parent), ui(new Ui::MaddeWindow) {
  ui->setupUi(this);
  this->setFixedHeight(this->height());
  this->setFixedWidth(this->width());

  _formantWindow = 0;

  for (int i = 0; i < MAXRECENTFILES; ++i) {
    _recentFileActs[i] = new QAction(this);
    _recentFileActs[i]->setVisible(false);
    this->ui->menuRecent_files->addAction(_recentFileActs[i]);
    connect(_recentFileActs[i], SIGNAL(triggered()), this,
            SLOT(openRecentFile()));
  }

  updateRecentFileActions();
  setWindowTitle("MaddeLOID");

  this->ui->gridLayout->addWidget(new QLabel("Enable"), 0, 0);
  this->ui->gridLayout->addWidget(new QLabel("FRQ(Hz)"), 1, 0);
  this->ui->gridLayout->addWidget(new QLabel("VOL(dB)"), 2, 0);
  this->ui->gridLayout->addWidget(new QLabel("Q"), 3, 0);
  this->ui->gridLayout->addWidget(new QLabel("BW"), 4, 0);

  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F1"), "UseF1"), 0, 1,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F2"), "UseF2"), 0, 2,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F3"), "UseF3"), 0, 3,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F4"), "UseF4"), 0, 4,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F5"), "UseF5"), 0, 5,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new QCheckBox("F6"), "UseF6"), 0, 6,
                                  1, 1);

  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F1"), 1, 1,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F2"), 1, 2,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F3"), 1, 3,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F4"), 1, 4,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F5"), 1, 5,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "F6"), 1, 6,
                                  1, 1);

  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A1"), 2, 1,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A2"), 2, 2,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A3"), 2, 3,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A4"), 2, 4,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A5"), 2, 5,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "A6"), 2, 6,
                                  1, 1);

  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q1"), 3, 1,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q2"), 3, 2,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q3"), 3, 3,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q4"), 3, 4,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q5"), 3, 5,
                                  1, 1);
  this->ui->gridLayout->addWidget(bindValue(new KawaiiLineEdit(), "Q6"), 3, 6,
                                  1, 1);

  this->ui->gridLayout->addWidget(bindValue(new QLabel("1"), "BW1"), 4, 1, 1,
                                  1);
  this->ui->gridLayout->addWidget(bindValue(new QLabel("2"), "BW2"), 4, 2, 1,
                                  1);
  this->ui->gridLayout->addWidget(bindValue(new QLabel("3"), "BW3"), 4, 3, 1,
                                  1);
  this->ui->gridLayout->addWidget(bindValue(new QLabel("4"), "BW4"), 4, 4, 1,
                                  1);
  this->ui->gridLayout->addWidget(bindValue(new QLabel("5"), "BW5"), 4, 5, 1,
                                  1);
  this->ui->gridLayout->addWidget(bindValue(new QLabel("6"), "BW6"), 4, 6, 1,
                                  1);

  this->ui->gridLayout_2->addWidget(new QLabel("gaindb"), 0, 0);
  this->ui->gridLayout_2->addWidget(new QLabel("slope"), 1, 0);
  this->ui->gridLayout_2->addWidget(new QLabel("slopedepthdb"), 2, 0);

  this->ui->gridLayout_2->addWidget(bindValue(new KawaiiLineEdit(), "gaindb"),
                                    0, 1, 1, 1);
  this->ui->gridLayout_2->addWidget(bindValue(new KawaiiLineEdit(), "slope"), 1,
                                    1, 1, 1);
  this->ui->gridLayout_2->addWidget(
      bindValue(new KawaiiLineEdit(), "slopedepthdb"), 2, 1, 1, 1);
}

QWidget* MaddeWindow::bindValue(QWidget* widget, QString keypath) {
  ValueBinder* b = new ValueBinder(widget, keypath, this);
  valueMap[keypath] = b;
  connect(b, SIGNAL(valueChanged(QString, QVariant)), this,
          SLOT(input_valueChanged(QString, QVariant)));
  return widget;
}

void MaddeWindow::setValue(QString key, QVariant value) {
  valueMap[key]->setValue(value);
  if ((key.length() == 2 && key[0] == 'Q')) {
    QString key1 = key;
    key1.replace("Q", "F");
    QString key2 = key;
    key2.replace("Q", "BW");
    float n = valueMap[key1]->_value.toFloat();
    float d = value.toFloat();
    if (n > 0 && d > 0)
      valueMap[key2]->setValue(n / d);
    else
      valueMap[key2]->setValue(0);
  }
  if ((key.length() == 2 && key[0] == 'F')) {
    QString key1 = key;
    key1.replace("F", "Q");
    QString key2 = key;
    key2.replace("F", "BW");
    float d = valueMap[key1]->_value.toFloat();
    float n = value.toFloat();
    if (n > 0 && d > 0)
      valueMap[key2]->setValue(n / d);
    else
      valueMap[key2]->setValue(0);
  }
  if ((key.length() == 3 && key[0] == 'B' && key[1] == 'W')) {
    QString key1 = key;
    key1.replace("BW", "F");
    QString key2 = key;
    key2.replace("BW", "Q");
    float d = valueMap[key1]->_value.toFloat();
    float n = value.toFloat();
    if (n > 0 && d > 0)
      valueMap[key2]->setValue(d / n);
    else
      valueMap[key2]->setValue(0);
  }
}

void MaddeWindow::input_valueChanged(QString key, QVariant value) {
  this->setValue(key, value);
  emit valueChanged(key, value);
}

MaddeWindow::~MaddeWindow() { delete ui; }

void MaddeWindow::closeEvent(QCloseEvent* event) {
  emit closed();
  QMainWindow::closeEvent(event);
}

void MaddeWindow::updateRecentFileActions() {
  QSettings settings("QTau","madde");
  QStringList files = settings.value("recentMaddeFileList").toStringList();

  int numRecentFiles = qMin(files.size(), (int)MAXRECENTFILES);

  bool visible = false;

  for (int i = 0; i < numRecentFiles; ++i) {
    QString fileName = QFileInfo(files[i]).fileName();
    QString text = tr("&%1 %2").arg(i + 1).arg(fileName);
    _recentFileActs[i]->setText(text);
    _recentFileActs[i]->setData(files[i]);
    _recentFileActs[i]->setVisible(true);
    visible = true;
  }
  for (int j = numRecentFiles; j < MAXRECENTFILES; ++j)
    _recentFileActs[j]->setVisible(false);

  ui->menuRecent_files->setEnabled(visible);
}

ValueBinder::ValueBinder(QWidget* widget, QString name, MaddeWindow* parent)
    : QObject(parent), _widget(widget), _name(name) {
  KawaiiLineEdit* lineEdit = qobject_cast<KawaiiLineEdit*>(widget);
  if (lineEdit) {
    connect(lineEdit, SIGNAL(editingFinished()), this,
            SLOT(lineEdit_editingFinished()));
    connect(lineEdit, SIGNAL(keyPressHooked(int)), this,
            SLOT(lineEdit_keyPressHooked(int)));
  }
  QCheckBox* checkBox = qobject_cast<QCheckBox*>(widget);
  if (checkBox) {
    connect(checkBox, SIGNAL(toggled(bool)), this,
            SLOT(checkBox_toggled(bool)));
  }
}

void ValueBinder::checkBox_toggled(bool value) {
  emit valueChanged(_name, value);
}

void ValueBinder::lineEdit_keyPressHooked(int key) {
  DEVLOG_DEBUG(_name);
  if (_name == "slope") {
    if (key == Qt::Key_Up) {
      emit valueChanged(_name, _value.toFloat() * 2);
    } else {
      emit valueChanged(_name, _value.toFloat() / 2);
    }
    return;
  }
  float increment = 10.0;
  if (_name.length() == 2 && _name[0] == 'A') increment = 1;
  if (_name.length() == 2 && _name[0] == 'Q') increment = 0.1;
  if (key == Qt::Key_Up) {
    emit valueChanged(_name, _value.toFloat() + increment);
  } else {
    emit valueChanged(_name, _value.toFloat() - increment);
  }
}

void ValueBinder::lineEdit_editingFinished() {
  KawaiiLineEdit* lineEdit = qobject_cast<KawaiiLineEdit*>(_widget);
  if (lineEdit) {
    emit valueChanged(_name, QVariant(lineEdit->text()));
  }
}

void ValueBinder::setValue(QVariant value) {
  _value = value;
  KawaiiLineEdit* lineEdit = qobject_cast<KawaiiLineEdit*>(_widget);
  if (lineEdit) {
#if 0
        if(_name=="slope") {
            float x = value.toFloat();
            QVariant value2 = ((int)(log(-x) * 100 + .5) / 100.0);
            lineEdit->setText(value2.toString());
        }
        else
#endif
    lineEdit->setText(value.toString());
  }
  QCheckBox* checkBox = qobject_cast<QCheckBox*>(_widget);
  if (checkBox) {
    checkBox->setChecked(value.toBool());
  }
  QLabel* label = qobject_cast<QLabel*>(_widget);
  if (label) {
    label->setText(value.toString() + "Hz");
  }
}

KawaiiLineEdit::KawaiiLineEdit() : QLineEdit() {}

void KawaiiLineEdit::keyPressEvent(QKeyEvent* event) {
  if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) {
    emit keyPressHooked(event->key());
  } else {
    // default handler for event
    QLineEdit::keyPressEvent(event);
  }
}

void MaddeWindow::on_actionNew_triggered() {
  _currentFileName = "";
  emit loadDefault();
  updateWindowTitle();
}

void MaddeWindow::addFileToRecentFiles(QString fileName) {
  DEVLOG_DEBUG("addFileToRecentFiles: " + fileName);

  QSettings settings;
  QStringList files = settings.value("recentMaddeFileList").toStringList();
  files.removeAll(fileName);
  files.prepend(fileName);
  while (files.size() > MAXRECENTFILES) files.removeLast();

  foreach (QString file, files)
    DEVLOG_DEBUG("recent: " + file);

  settings.setValue("recentMaddeFileList", files);

  updateRecentFileActions();
}

void MaddeWindow::on_actionOpen_triggered() {
  QString fileName = QFileDialog::getOpenFileName(
      this, tr("Open Madde File"), "", tr("Madde Files (*.madde)"));

  if (!fileName.isEmpty()) {
    addFileToRecentFiles(fileName);
    emit loadFile(fileName);
    _currentFileName = fileName;
    _lastMaddeDir = QFileInfo(fileName).absolutePath();
    updateWindowTitle();
  }
}

void MaddeWindow::openRecentFile() {
  QAction* action = qobject_cast<QAction*>(sender());
  if (action) {
    QString fileName = action->data().toString();
    if (QFile(fileName).exists()) {
      emit loadFile(fileName);
      _currentFileName = fileName;
      _lastMaddeDir = QFileInfo(fileName).absolutePath();
      updateWindowTitle();
    } else {
      QSettings settings;
      QStringList files = settings.value("recentMaddeFileList").toStringList();
      files.removeOne(fileName);
      settings.setValue("recentMaddeFileList", files);
      updateRecentFileActions();

      QMessageBox msgBox;
      msgBox.setText("The file \"" + fileName + "\"does not exist.");
      msgBox.exec();
    }
  }
}

void MaddeWindow::on_actionSave_triggered() {
  if (_currentFileName.isEmpty())
    on_actionSave_as_triggered();
  else
    emit saveToFile(_currentFileName);
}

void MaddeWindow::on_actionSave_as_triggered() {
  QString fileName = QFileDialog::getSaveFileName(
      this, tr("Save Madde File"), _lastMaddeDir, tr("Madde Files (*.madde)"));
  if (!fileName.isEmpty()) {
    emit saveToFile(fileName);
    _currentFileName = fileName;
    _lastMaddeDir = QFileInfo(fileName).absolutePath();
    updateWindowTitle();
  }
}

void MaddeWindow::on_actionShow_F1_F2_map_triggered() {
  if (_formantWindow) return;

  _formantWindow = new FormantMapWindow();
  _formantWindow->show();
  connect(_formantWindow, SIGNAL(mouseDown(float, float)), this,
          SLOT(formantWindow_mouseDown(float, float)));
  connect(_formantWindow, SIGNAL(mouseUp()), this,
          SLOT(formantWindow_mouseUp()));
  connect(_formantWindow, SIGNAL(closed()), this, SLOT(formantWindow_closed()));
}

void MaddeWindow::formantWindow_mouseDown(float f1, float f2) {
  input_valueChanged("F1", f1);
  input_valueChanged("F2", f2);
  emit mouseEvent(1);
}

void MaddeWindow::formantWindow_mouseUp() { emit mouseEvent(0); }

void MaddeWindow::formantWindow_closed() {
  delete _formantWindow;
  _formantWindow = NULL;
}

void MaddeWindow::updateWindowTitle() {
  QString fileName = QFileInfo(_currentFileName).fileName();
  if (fileName.length() > 0)
    setWindowTitle(fileName + " - MaddeLOID");
  else
    setWindowTitle("MaddeLOID");
}
